package ui.swing.textcomponent.autocompleting;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class CompletableJTextField extends JTextField implements
		ListSelectionListener {

	Completer completer;
	JList completionList;
	DefaultListModel completionListModel;
	JScrollPane listScroller;
	JWindow listWindow;

	public CompletableJTextField(int col) {
		super(col);
		completer = new Completer();
		completionListModel = new DefaultListModel();
		completionList = new JList(completionListModel);
		completionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		completionList.addListSelectionListener(this);
		listScroller = new JScrollPane(completionList,
				ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

		listWindow = new JWindow();
		listWindow.setAlwaysOnTop(true);
		listWindow.getContentPane().add(listScroller);
	}

	public void addCompletion(String s) {
		completer.addCompletion(s);
	}

	public void removeCompletion(String s) {
		completer.removeCompletion(s);
	}

	public void clearCompletions(String s) {
		completer.clearCompletions();
	}

	public void valueChanged(ListSelectionEvent e) {
		if (e.getValueIsAdjusting()) {
			return;
		}
		if (completionList.getModel().getSize() == 0) {
			return;
		}
		listWindow.setVisible(false);
		final String completionString = (String) completionList
				.getSelectedValue();
		
		// ??? 为何不直接setText？这是一个LongTask吗？
		Thread worker = new Thread() {
			@Override
			public void run() {
				setText(completionString);
			}
		};
		SwingUtilities.invokeLater(worker);
	}

	/**
	 * inner class does the matching of the JTextField's document to completion
	 * strings kept in an ArrayList
	 */
	class Completer implements DocumentListener {

		private Pattern pattern;
		private ArrayList completions;

		public Completer() {
			completions = new ArrayList();
			CompletableJTextField.this.getDocument().addDocumentListener(this);
		}

		public void removeCompletion(String s) {
			completions.remove(s);
			buildAndShowPopup();
		}

		public void addCompletion(String s) {
			completions.add(s);
			buildAndShowPopup();
		}

		public void clearCompletions() {
			completions.clear();
			buildPopup();
			listWindow.setVisible(false);
		}

		private void buildPopup() {
			completionListModel.clear();
			System.out.println("buildPopup for " + completions.size()
					+ "completions");
			Iterator it = completions.iterator();

			// @@@@@@@~~~~很好很强大
			pattern = Pattern.compile(getText() + ".+");
			while (it.hasNext()) {
				// check if match
				String completion = (String) it.next();
				Matcher matcher = pattern.matcher(completion);
				if (matcher.matches()) {
					// add if match
					System.out.println("matched " + completion);
					completionListModel.addElement(completion);
					/*
					 * completionListModel.add (completionListModel.getSize(),
					 * completion);
					 */
				} else {
					System.out.println("pattern does not match " + completion);
				}
			}
		}

		private void showPopup() {
			if (completionListModel.getSize() == 0) {
				listWindow.setVisible(false);
				return;
			}
			// figure out where the text field is,
			// and where its bottom left is
			Point los = CompletableJTextField.this.getLocationOnScreen();
			int popX = los.x;
			int popY = los.y + getHeight();
			listWindow.setLocation(popX, popY);
			listWindow.pack();
			listWindow.setVisible(true);
		}

		private void buildAndShowPopup() {
			if (CompletableJTextField.this.getText().length() < 1)
				return;
			buildPopup();
			showPopup();
		}

		// DocumentListener implementation
		public void insertUpdate(DocumentEvent e) {
			buildAndShowPopup();
		}

		public void removeUpdate(DocumentEvent e) {
			buildAndShowPopup();
		}

		public void changedUpdate(DocumentEvent e) {
			buildAndShowPopup();
		}
	}
}
